package scheduler

import (
	"time"

	"github.com/robfig/cron/v3"
	"github.com/yourusername/rss-reader-ai/internal/model"
	"github.com/yourusername/rss-reader-ai/internal/repository"
	"github.com/yourusername/rss-reader-ai/internal/service"
	"go.uber.org/zap"
)

// Scheduler 定时任务调度器
type Scheduler struct {
	cron        *cron.Cron
	feedService service.FeedService
	feedRepo    repository.FeedRepository
	logger      *zap.Logger
}

// NewScheduler 创建调度器
func NewScheduler(
	feedService service.FeedService,
	feedRepo repository.FeedRepository,
	logger *zap.Logger,
) *Scheduler {
	// 使用带秒级精度的 cron
	c := cron.New(cron.WithSeconds())

	return &Scheduler{
		cron:        c,
		feedService: feedService,
		feedRepo:    feedRepo,
		logger:      logger,
	}
}

// Start 启动定时任务
func (s *Scheduler) Start() error {
	s.logger.Info("📅 启动定时任务调度器...")

	// 添加定时任务：每小时抓取一次
	// Cron 表达式：秒 分 时 日 月 周
	// "0 0 * * * *" 表示每小时的第0分0秒执行
	_, err := s.cron.AddFunc("0 0 * * * *", func() {
		s.FetchAllFeeds()
	})
	if err != nil {
		s.logger.Error("❌ 添加定时任务失败", zap.Error(err))
		return err
	}

	// 启动 cron
	s.cron.Start()
	s.logger.Info("✅ 定时任务调度器已启动（每小时抓取一次）")

	// 启动时立即执行一次抓取（可选）
	go func() {
		time.Sleep(5 * time.Second) // 等待服务器完全启动
		s.logger.Info("🚀 执行启动时的初始抓取...")
		s.FetchAllFeeds()
	}()

	return nil
}

// Stop 停止定时任务
func (s *Scheduler) Stop() {
	s.logger.Info("⏸️  停止定时任务调度器...")
	ctx := s.cron.Stop()
	<-ctx.Done() // 等待所有任务完成
	s.logger.Info("✅ 定时任务调度器已停止")
}

// FetchAllFeeds 抓取所有启用的订阅源（并发 + 重试）
func (s *Scheduler) FetchAllFeeds() {
	startTime := time.Now()
	s.logger.Info("==========================")
	s.logger.Info("🔄 开始定时抓取所有订阅源...")

	// 获取所有启用的订阅源
	feeds, err := s.feedRepo.GetActiveFeeds()
	if err != nil {
		s.logger.Error("❌ 获取订阅源列表失败", zap.Error(err))
		return
	}

	if len(feeds) == 0 {
		s.logger.Info("ℹ️  没有启用的订阅源，跳过抓取")
		return
	}

	s.logger.Info("📋 找到订阅源",
		zap.Int("total", len(feeds)),
	)

	// 使用 channel 控制并发数（最多5个并发）
	const maxConcurrency = 5
	semaphore := make(chan struct{}, maxConcurrency)
	
	// 统计信息（需要并发安全）
	type result struct {
		success      bool
		feedID       int64
		feedTitle    string
		newArticles  int
		err          error
	}
	results := make(chan result, len(feeds))

	// 并发抓取所有 Feed
	for _, feed := range feeds {
		semaphore <- struct{}{}  // 获取令牌
		
		go func(f *model.Feed) {
			defer func() { <-semaphore }()  // 释放令牌
			
			// 重试逻辑：最多尝试 3 次
			var newCount int
			var err error
			maxRetries := 3
			
			for attempt := 1; attempt <= maxRetries; attempt++ {
				newCount, err = s.feedService.RefreshFeed(f.ID)
				
				if err == nil {
					// 成功，记录结果
					results <- result{
						success:     true,
						feedID:      f.ID,
						feedTitle:   f.Title,
						newArticles: newCount,
					}
					return
				}
				
				// 失败，记录日志
				s.logger.Warn("⚠️  抓取失败，准备重试",
					zap.Int64("feed_id", f.ID),
					zap.String("title", f.Title),
					zap.Int("attempt", attempt),
					zap.Int("max_retries", maxRetries),
					zap.Error(err),
				)
				
				// 指数退避：1秒 → 2秒 → 4秒
				if attempt < maxRetries {
					backoff := time.Duration(1<<uint(attempt-1)) * time.Second
					time.Sleep(backoff)
				}
			}
			
			// 所有重试都失败
			results <- result{
				success:   false,
				feedID:    f.ID,
				feedTitle: f.Title,
				err:       err,
			}
		}(feed)
	}

	// 等待所有任务完成并收集结果
	successCount := 0
	failCount := 0
	totalNewArticles := 0
	
	for i := 0; i < len(feeds); i++ {
		res := <-results
		
		if res.success {
			s.logger.Info("✅ 抓取成功",
				zap.Int64("feed_id", res.feedID),
				zap.String("title", res.feedTitle),
				zap.Int("new_articles", res.newArticles),
			)
			successCount++
			totalNewArticles += res.newArticles
		} else {
			s.logger.Error("❌ 抓取失败（重试3次后仍失败）",
				zap.Int64("feed_id", res.feedID),
				zap.String("title", res.feedTitle),
				zap.Error(res.err),
			)
			failCount++
		}
	}

	// 输出统计信息
	duration := time.Since(startTime)
	s.logger.Info("==========================")
	s.logger.Info("📊 抓取完成",
		zap.Int("success", successCount),
		zap.Int("failed", failCount),
		zap.Int("total", len(feeds)),
		zap.Int("new_articles", totalNewArticles),
		zap.Duration("duration", duration),
	)
	s.logger.Info("==========================")
}
